#include "../include/TimeLine.h"

BEGIN_EVENT_TABLE(TimeLine, wxPanel)
    EVT_PAINT(TimeLine::paintEvent)
    EVT_SIZE(TimeLine::resize)
    EVT_MOTION(TimeLine::mouseMoved)
    EVT_ERASE_BACKGROUND(TimeLine::eraseBackground)

    EVT_LEFT_DOWN(TimeLine::mouseLeftDown)
    EVT_LEFT_UP(TimeLine::mouseReleased)
    EVT_RIGHT_DOWN(TimeLine::mouseRightDown)
    EVT_MIDDLE_DOWN(TimeLine::mouseMiddleDown)
END_EVENT_TABLE()

TimeLine::TimeLine(wxWindow* parent,
                   Config* config,
                   ProjectFrame* projectFrame,
                   AnimationManager* animMgr,
                   wxWindowID id,
                   const wxPoint& pos,
                   long style,
                   const wxString& name)
:wxPanel(parent, id, pos, wxDefaultSize, style, name)
{
    SetMinSize( wxSize(400,50) );

    this->animMgr       = animMgr;
    this->config        = config;
    sprite              = NULL;
    dragging            = FALSE;
    dragStart           = 0;
    displacement        = 0;
    tapeMargin.x        = 6;
    tapeMargin.y        = 30;
    elemSize            = 8;
    elemInterval        = 1;
    tapeHeight          = 16;
    tapeInnerMargin     = 3;
    hovered             = 0;

    wxBitmap temp_bmp( _("./slider/gui.png"), wxBITMAP_TYPE_PNG );
    sprite = new wxMemoryDC( temp_bmp );
}


TimeLine::~TimeLine()
{
    //dtor
}

void TimeLine::reset()
{
    dragging        = FALSE;
    dragStart       = 0;
    displacement    = 0;
    Refresh();
}

void TimeLine::setAnimationManager( AnimationManager* animMgr)
{
    this->animMgr = animMgr;
}

void TimeLine::setConfig( Config* config)
{
    this->config = config;
}

void TimeLine::draw()
{
    wxBufferedPaintDC dc(this);
    PrepareDC(dc);
    dc.Clear();

    wxColour backgroundColour = wxColour(245,245,245);
    dc.SetBackground(backgroundColour);

    //draw backgound
    {
        // tape's front
        dc.Blit( tapeMargin.x, tapeMargin.y, 10, tapeHeight, sprite, 0, 0);
        // tape's end
        dc.Blit( GetSize().GetWidth()-tapeMargin.x-10, tapeMargin.y, 10, tapeHeight, sprite, 11, 0);

        // body texture
        for(int i= tapeMargin.x+10; i<=GetSize().GetWidth()-tapeMargin.x-10; ++i)
            dc.Blit( i, tapeMargin.y, 1, tapeHeight, sprite, 10, 0);
    } // end draw brackground

    if(config && animMgr)
    {
        wxString dist;
        if( hovered<0)
            dist << 0;
        else
            dist << hovered;

        dc.DrawText(dist , 10, 10);

        int moveY = 0;
        unsigned noFrames = config->getLength()*config->getFPS();

        dc.SetClippingRegion( tapeMargin.x+tapeInnerMargin, tapeMargin.y, dc.GetSize().GetWidth()-2*tapeMargin.x-6, dc.GetSize().GetHeight()-tapeMargin.y);

        unsigned currentFrame = displacement/(elemSize+elemInterval);

        std::map <unsigned long, std::vector< BoneState*>* >::const_iterator mapIterator;
        mapIterator = animMgr->getFramesInfo().lower_bound(currentFrame);

        for( unsigned i=currentFrame; i<=noFrames; ++i)
        {
            if( i == (hovered) )
                moveY = -1;

            bool notPrinted = TRUE;
            if( !animMgr->getFramesInfo().empty() )
            {
                if(i == ( *(mapIterator++) ).first )
                {
                    dc.Blit( tapeMargin.x+tapeInnerMargin - displacement%(elemSize+elemInterval) + (i-currentFrame)*(elemSize+elemInterval) -1, tapeMargin.y+4-1 + moveY, 10, 10, sprite, 29, 0);
                    notPrinted = FALSE;
                }
                else
                    mapIterator--;
            }

            if( notPrinted )
            {
                dc.Blit( tapeMargin.x+tapeInnerMargin - displacement%(elemSize+elemInterval) + (i-currentFrame)*(elemSize+elemInterval), tapeMargin.y+4 + moveY, 8, 8, sprite, 21, 0);
            }

            moveY = 0;
        }

        // draw foregound
        {
            dc.DestroyClippingRegion();
            // tape's front
            dc.Blit( tapeMargin.x, tapeMargin.y, 76, 14, sprite, 39, 0);
            // tape's end
            dc.Blit( GetSize().GetWidth()-tapeMargin.x-76, tapeMargin.y, 76, 14, sprite, 115, 0);
        }
        // end draw foreground
    }
}

void TimeLine::paintEvent(wxPaintEvent & evt)
{
    draw();
}

void TimeLine::resize(wxSizeEvent & evt)
{
    Refresh();
}

void TimeLine::eraseBackground(wxEraseEvent& evt)
{

}

void TimeLine::mouseMoved(wxMouseEvent& evt)
{
    bool isHovering = FALSE;
    if(config && animMgr)
    {
        // check if it is hovered over the time tape (in y)
        if( evt.GetPosition().y >= tapeMargin.y && evt.GetPosition().y <= tapeMargin.y + tapeHeight)
        {
            // check if it is hovered over the tape (in x)
            if( evt.GetPosition().x >tapeMargin.x+tapeInnerMargin && evt.GetPosition().x < this->GetSize().GetWidth()-2*tapeMargin.x+2 )
            {
                //if( (evt.GetPosition().x + displacement)%elemSize < elemInterval)
                {
                    // count the number of the hovered element
                    hovered = (evt.GetPosition().x + displacement)/(elemSize+elemInterval)-1;
                    Refresh();
                    isHovering = TRUE;
                }
            }
        }

        if ( !isHovering )
        {
            hovered = -1;
            Refresh();
        }


        if( evt.Dragging() && dragging)
        {
            int delta = dragStart - evt.GetPosition().x;
            dragStart = evt.GetPosition().x;

            if( delta < 0)
            {
                // d definition is only becouse of warning coused of comparing unsigned to signed value
                unsigned d = -delta;
                if( displacement < d)
                {
                    displacement = 0;
                    Refresh();
                    return;
                }
            }

            displacement += delta;
            if( displacement > config->getLength()*config->getFPS()*(elemSize+elemInterval)+1 )
                displacement = config->getLength()*config->getFPS()*(elemSize+elemInterval)+1;
            Refresh();

        }
    }
}

void TimeLine::mouseLeftDown(wxMouseEvent& evt)
{
    dragging = TRUE;
    dragStart = evt.GetPosition().x;

    wxCursor cursor(wxCURSOR_HAND);
    SetCursor( cursor );
}

void TimeLine::mouseReleased(wxMouseEvent& evt)
{
    if( hovered >=0 )
    {
        animMgr->showFrame( hovered );
    }

    dragging = FALSE;

    wxCursor cursor(wxCURSOR_ARROW);
    SetCursor( cursor );
}

void TimeLine::mouseRightDown(wxMouseEvent& evt)
{
    if( hovered >= 0 )
    {
        wxString tmp;
        tmp << hovered;
        //wxMessageBox( tmp, tmp);
        animMgr->addState( hovered );
        Refresh();
    }
}

void TimeLine::mouseMiddleDown(wxMouseEvent& evt)
{
    if( hovered >= 0 )
    {
        animMgr->deleteState( hovered );
        Refresh();
    }
}
